﻿// Copyright 2009 Mike Geise
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Falcon.Core.Extensions
{
	using System;
	using System.Collections.Generic;
	using System.Globalization;
	using System.Linq;
	using Falcon.Core.Authentication;
	using Falcon.Core.Domain.Enums;

	public static class DateExtension
	{
		// private fields
		private static readonly Dictionary<double, Func<TimeSpan, string>> Map;

		/// <summary>
		/// Initializes the <see cref="DateExtension"/> class.
		/// </summary>
		static DateExtension()
		{
			Map = new Dictionary<double, Func<TimeSpan, string>>
			{
				{ 0.75, x => "less than a minute ago" },
				{ 1.5, x => "about a minute ago" },
				{ 45, x => "about {0} minutes ago".Fmt(Math.Round(x.TotalMinutes)) },
				{ 90, x => "about an hour ago" },
				{ 60 * 24, x => "about {0} hours ago".Fmt(Math.Round(Math.Abs(x.TotalHours))) },
				{ 60 * 48, x => "about a day ago" },
				{ 60 * 24 * 30, x => "about {0} days ago".Fmt(Math.Floor(Math.Round(x.TotalDays))) },
				{ 60 * 24 * 60, x => "about a month ago" },
				{ 60 * 24 * 365, x => "about {0} months ago".Fmt(Math.Floor(Math.Round(x.TotalDays / 30))) },
				{ 60 * 24 * 365 * 2, x => "about a year ago" },
				{ Double.MaxValue, x => "about {0} years ago".Fmt(Math.Floor(Math.Round(x.TotalDays / 365))) }
			};
		}

		/// <summary>
		/// Personalizes the specified date.
		/// </summary>
		/// <param name="date">The date.</param>
		/// <param name="identity">The identity.</param>
		/// <returns></returns>
		public static string Personalize(this DateTime date, WebUserIdentity identity)
		{
			if (identity != null)
			{
				var adjusted = Adjust(date, identity);

				switch (identity.Session.TimeFormat)
				{
					case UserPreferenceTimeFormat.Relative:
						return adjusted.Ago(identity);
					case UserPreferenceTimeFormat.Extended:
						return adjusted.ToString("MMM d, yyyy h:mm tt", CultureInfo.InvariantCulture);
					default:
						return adjusted.ToString("M/d/yyyy h:mm tt", CultureInfo.InvariantCulture);
				}
			}

			return date.ToString("MMM d, yyyy h:mm tt", CultureInfo.InvariantCulture);
		}

		/// <summary>
		/// Adjusts the specified date.
		/// </summary>
		/// <param name="date">The date.</param>
		/// <param name="identity">The identity.</param>
		/// <returns></returns>
		public static DateTime Adjust(this DateTime date, WebUserIdentity identity)
		{
			if (identity != null)
			{
				var zoneF = TimeZoneInfo.Utc;
				var zoneT = TimeZoneInfo.FindSystemTimeZoneById(identity.Session.TimeZone);

				return TimeZoneInfo.ConvertTime(date.ToUniversalTime(), zoneF, zoneT);
			}

			return date;
		}

		/// <summary>
		/// Gets the specified date in relative form.
		/// </summary>
		/// <param name="date">The date.</param>
		/// <param name="identity">The identity.</param>
		/// <returns></returns>
		public static string Ago(this DateTime date, WebUserIdentity identity)
		{
			var diff = DateTime.Now.Adjust(identity).Subtract(date.Adjust(identity));
			var minutes = diff.TotalMinutes;

			if (minutes < 0.0)
			{
				minutes = Math.Round(minutes);
			}

			return Map.First(n => minutes < n.Key).Value(diff);
		}
	}
}